#!/usr/bin/env python
# -*- coding:utf8 -*-
"""
Author : mafei
@Date   : 2019/11/30

"""
import time
import pprint
import logging
from mmonitor.common.http_request import request_get_url
from mmonitor.utils.cache import ListInteractor
from mmonitor.main.mail_send import Send
from mmonitor.models.mysql_db import WebSiteModel, HistoryModel, ErrorHistoryModel, AlertStatusModel
from mmonitor.common import vars as vr


class SpiderBase(object):

    def __init__(self):
        self.lcache = ListInteractor()
        self.mail_send = Send()

    @property
    def now(self):
        return int(time.time() * 1000)

    def get_url(self, url_id):
        url_info = WebSiteModel.get_by_id(url_id)
        if not url_info:
            logging.info("can't get url by url_id:{}".format(url_id))
            return ""
        url = url_info.url
        logging.info("start get url %s" % url)
        if 'http' not in url:
            url = "http://" + url
        return url

    def run(self, url_id):

        url = self.get_url(url_id)
        if not url:
            logging.error("can't get url by url_id:{}".format(url_id))
            return
        status_code, error, duration = request_get_url(url)
        self.save_result(url_id, status_code, error, duration)
        self.notify(url_id, status_code, error, duration)

    def save_result(self, url_id, status_code, error, duration):
        self.save_history(url_id, status_code, error, duration)
        if error:
            self.save_error(url_id, error)

    def save_history(self, url_id, status_code, error, duration):
        data = {
            "item_id": url_id,
            "ts": self.now,
            "status_code": status_code,
            "duration": duration
        }
        logging.info('start save :{} duration:{} status_code:{}'.format(url_id, duration, status_code))
        HistoryModel.create(**data)

    def is_new_error(self, url_id, error):
        is_new_error = True
        if AlertStatusModel.get_or_none(AlertStatusModel.item_id == url_id):
            is_new_error = False
        return is_new_error

    def save_error(self, url_id, error):
        logging.info('start save error url_id:{} error:{}'.format(url_id, error))
        if self.is_new_error(url_id, error):
            self.save_new_error(url_id, error)
        else:
            self.update_error(url_id, error)

    def update_error(self, url_id, error):
        now_error = ErrorHistoryModel.select().where(ErrorHistoryModel.item_id == url_id).order_by(ErrorHistoryModel.update_time.desc()).get()
        if not now_error:
            logging.error("update alert but not found :{}".format(url_id))
            return
        logging.info("update error :{} error:{}".format(url_id, error))
        ErrorHistoryModel.update(**{"update_time": self.now}).where(ErrorHistoryModel.id == now_error.id).execute()

    def save_new_error(self, url_id, error):
        error_data = {
            "item_id": url_id,
            "error": error,
            "start_time": self.now,
            "update_time": self.now
        }
        '''
        {'item_id': 15, 'start_time': 1577894556943, 'update_time': 1577894556943, 'error': 1}
        '''

        logging.info('start save new error url_id:{} error:{}'.format(url_id, error))
        ErrorHistoryModel.create(**error_data)
        AlertStatusModel.create(**{'item_id': url_id, "error": error})

    def notify(self, url_id, status_code, error, duration):
        """
        确认要不要发预警/恢复通知

        预警条件
            1、这次是故障的
            2、查上次的结果已经出现预警了
            3、上次结果已经超过20分钟了
            4、距离上次通知已经过了1小时了
        预警动作：
            1、发邮件
            2、更新alert_status最近一次发邮件为当前时间

        恢复通知条件：
            1、这一次是正常的
            2、alert_status表里面有记录，代表现在有故障
        恢复动作：
            1、发邮件
            2、删除alert_status表中的记录，代表故障恢复

        """
        logging.info("start notify url_id:{} error:{}".format(url_id, error))
        res = AlertStatusModel.get_or_none(AlertStatusModel.item_id == url_id)
        if not res:
            logging.info("alert status table not exist this url_id:{}".format(url_id))
            return
        if error:
            self.alert(url_id, status_code, error, res.ts, res.last_notify)
        else:
            self.recovery_notify(url_id, error, res.ts)

    def alert(self, url_id, status_code, error, error_ts, last_notify):
        logging.info('start check alert by url_id:{}'.format(url_id))
        error_ts = time.mktime(error_ts.timetuple()) * 1000
        if self.now - error_ts < 20*60:
            logging.info('这一次距离上一次没超过20分钟，先不发')
            return
        if last_notify and self.now - last_notify < 60*60*1000:
            logging.info('这一次通知距离上一次不足一小时，先不发')
            return
        self.send_alert(url_id, error, self.now - error_ts)
        AlertStatusModel.update({AlertStatusModel.last_notify: self.now}).where(AlertStatusModel.item_id == url_id).execute()

    def recovery_notify(self, url_id, error, error_ts):
        error_ts = time.mktime(error_ts.timetuple()) * 1000
        self.mail_send.process(url_id, error, self.now - error_ts, vr.s_recovery_type)
        AlertStatusModel.delete().where(AlertStatusModel.item_id == url_id).execute()

    def send_alert(self, url_id, error, error_ts):
        """

        """
        logging.info('start send alert by url_id:{}, error:{}, error_ts:{}'.format(url_id, error, error_ts))
        self.mail_send.process(url_id, error, error_ts, vr.s_alert_type)


def run_job(url_id):
    spider = SpiderBase()
    spider.run(url_id)


if __name__ == "__main__":
    logging.basicConfig(
        format="%(asctime)s %(name)s[%(module)s#%(lineno)d] %(levelname)s %(message)s",
        level=logging.INFO)
    spider = SpiderBase()
    while True:
        spider.run(4)
        # for website in WebSiteModel.select():
        #     spider.run(website.id)
        logging.info("sleep 5minute.... wait for next")
        time.sleep(5*60)
